package com.hue.springbootmybatis.plugins;

import org.apache.ibatis.binding.MapperMethod;
import org.apache.ibatis.binding.MapperRegistry;
import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.type.TypeHandlerRegistry;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;

/**
 * @Author: 姚凤桥
 * @Date: 2022/5/27 20:18
 * @Description: Sql语句打印插件
 */
@Component
@Intercepts({
        @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}),
        @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
        @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}),
})
public class SqlPrintPlugins implements Interceptor {
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        Object[] args = invocation.getArgs();
        //获取映射语句和参数
        MappedStatement mappedStatement = (MappedStatement) args[0];
        Object parameters = args[1];

        Object res = null;
        long start = System.currentTimeMillis();
        try {
            res = invocation.proceed();
        } finally {
            long end = System.currentTimeMillis();
            System.out.println("执行时间：" + (end - start));
            String sql = parseSql(mappedStatement, parameters);
            System.out.println("执行的sql语句：" + sql);
        }
        return res;
    }

    /**
     * 解析sql语句
     *
     * @param mappedStatement
     * @param parameters
     * @return
     */
    private String parseSql(MappedStatement mappedStatement, Object parameters) {
        BoundSql boundSql = mappedStatement.getBoundSql(parameters);
        String sql = boundSql.getSql();
        if (StringUtils.isEmpty(sql)) {
            return "";
        }

        //美化sql
        sql = beautifulSql(sql);
        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
        //如果有参数需要替换，将占位符替换为值
        if (!parameterMappings.isEmpty() && parameters != null) {
            //获取类型注册器
            TypeHandlerRegistry typeHandlerRegistry = mappedStatement.getConfiguration().getTypeHandlerRegistry();
            if (typeHandlerRegistry.hasTypeHandler(parameters.getClass())) {
                //将占位符替换为值
                sql = replaceValue(sql, parameters);
            } else {
                //不是基本类型
                MetaObject metaObject = mappedStatement.getConfiguration().newMetaObject(parameters);
                for (ParameterMapping parameterMapping : parameterMappings) {
                    String property = parameterMapping.getProperty();
                    if (metaObject.hasGetter(property)) {
                        Object value = metaObject.getValue(property);
                        sql = replaceValue(sql, value);
                    } else if (boundSql.hasAdditionalParameter(property)) {
                        Object value = boundSql.getAdditionalParameter(property);
                        sql = replaceValue(sql, value);
                    }
                }
            }
        }

        return sql;
    }

    /**
     * 将占位符替换为值
     *
     * @param sql
     * @param parameterObject
     * @return
     */
    private String replaceValue(String sql, Object parameterObject) {
        String value = "";
        if (StringUtils.isEmpty(sql) || parameterObject == null) {
            return sql;
        }

        if (parameterObject instanceof String) {
            value = "'" + parameterObject + "'";
        } else if (parameterObject instanceof Date) {
            value = "'" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(parameterObject) + "'";
        } else {
            value = parameterObject.toString();
        }
        return sql.replaceFirst("\\?", value);
    }

    /**
     * 将连续的多个空格和换行符替换为空格
     *
     * @param sql
     * @return
     */
    private String beautifulSql(String sql) {
        return sql.replaceAll("[\\s\n]+", " ");
    }
}
